package com.dingwen.stusprboomon.dao;

import com.dingwen.stusprboomon.annotation.Check;
import com.dingwen.stusprboomon.annotation.ParamCheckAnnotation;
import com.dingwen.stusprboomon.constant.eum.ParamEnum;
import com.dingwen.stusprboomon.entity.page.MongoPage;
import com.dingwen.stusprboomon.entity.page.MongoPageable;
import com.dingwen.stusprboomon.util.ParamCheckUtil;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * dao 基类 对MongoTemplate的封装
 *
 * @author dingwen
 * 2021.06.23 14:18
 */
public class MongoBaseDao<T> {

    /**
     * mongo 模板
     */
    private final MongoTemplate mongoTemplate;


    @Autowired
    public MongoBaseDao(MongoTemplate mongoTemplate) {
        this.mongoTemplate = mongoTemplate;
    }

    /*
     ******************************** 查询 *************************************************
     */

    /**
     * 查询所有
     *
     * @return {@link List<T>}
     */
    public List<T> findAll() {
        return mongoTemplate.findAll(getEntityClass());
    }

    /**
     * 根据ID集合查询列表
     *
     * @param ids id
     * @return {@link List<T>}
     */
    public List<T> findList(List<String> ids) {
        return findList(new Query().addCriteria(Criteria.where("_id").in(ids)));
    }

    /**
     * 根据条件查询列表
     *
     * @param query 查询
     * @return {@link List<T>}
     */
    public List<T> findList(Query query) {
        return mongoTemplate.find(query, getEntityClass());
    }


    /**
     * 根据条件查询单个
     *
     * @param query 查询
     * @return {@link T}
     */
    public T findOne(Query query) {
        return mongoTemplate.findOne(query, getEntityClass());
    }

    /**
     * 根据列名和值查询当个文档
     *
     * @param filed 属性名称
     * @param value 价值
     * @return {@link T}
     */
    public T findOne(String filed, String value) {
        return mongoTemplate.findOne(new Query().addCriteria(Criteria.where(filed).is(value)), getEntityClass());
    }

    /**
     * 根据ID查询
     *
     * @param id id
     * @return {@link T}
     */
    public T findOne(String id) {
        return findOne("_id", id);
    }


    /**
     * 分页查询
     *
     * @param query    查询
     * @param sort     排序
     * @param pageSize 页面大小
     * @param pageNum  页面num
     * @return {@link PageImpl<T>}
     */
//    @ParamCheckAnnotation(value = {ParamEnum.SORT, ParamEnum.PAGE_SIZE, ParamEnum.PAGE_NUM})
    public MongoPage<T> findPage(Sort sort, @Check(defaultValue = "10") Integer pageSize, Integer pageNum, Query query) {

        // 总记录条数
        long totalCount = mongoTemplate.count(query, this.getEntityClass());

        // 分页对象
        MongoPageable pageable = MongoPageable.builder()
                .pageNum(pageNum)
                .pagSize(pageSize)
                .sort(sort).build();

        query.with(pageable);


        List<T> tList = findList(query);

        PageImpl<T> page = new PageImpl<>(tList, pageable, totalCount);

        MongoPage<T> mongoPage = new MongoPage<>();
        mongoPage.setData(page.getContent());
        mongoPage.setPageNum(page.getNumber());
        mongoPage.setPageSize(page.getSize());
        mongoPage.setTotalNum(page.getTotalElements());
        mongoPage.setTotalPageNum(page.getTotalPages());
        return mongoPage;
    }


    /*
     ******************************** 添加 *************************************************
     */

    /**
     * 添加
     *
     * @param t t
     * @return {@link T}
     */
    public T sava(T t) {
        return mongoTemplate.save(t);
    }


    /**
     * 保存所有
     *
     * @param collection 集合
     * @return {@link List<T>}
     */
    public List<T> saveAll(Collection<T> collection) {
        return (List<T>) mongoTemplate.insertAll(collection);
    }


    /*
     ******************************** 删除 *************************************************
     */

    /**
     * 删除通过id
     *
     * @param id id
     * @return {@link DeleteResult}
     */
    public DeleteResult removeById(String id) {
        return remove("_id", id);
    }

    /**
     * 通过字段值匹配删除
     *
     * @param filed 提起
     * @param value 价值
     * @return {@link DeleteResult}
     */
    public DeleteResult remove(String filed, String value) {
        return mongoTemplate.remove(new Query().addCriteria(Criteria.where(filed).is(value)), getEntityClass());
    }

    /**
     * 根据集合内容删除
     *
     * @param t t
     * @return {@link DeleteResult}
     */
    public DeleteResult remove(T t) {
        return mongoTemplate.remove(t);
    }

    /**
     * 根据ids进行删除
     *
     * @param ids id
     * @return {@link DeleteResult}
     */
    public DeleteResult removeByIds(List<String> ids) {
        return mongoTemplate.remove(new Query().addCriteria(Criteria.where("_id").in(ids)));
    }

    /*
     ******************************** 修改 *************************************************
     */

    /**
     * 修改
     *
     * @param query  查询
     * @param update 更新
     * @return {@link UpdateResult}
     */
    public UpdateResult modify(Query query, Update update) {
        return mongoTemplate.upsert(query, update, getEntityClass());
    }


    /**
     * 修改
     *
     * @param query   查询
     * @param options 选项
     * @return {@link UpdateResult}
     */
    public UpdateResult modify(Query query, Map<String, Object> options) {
        Update update = new Update();
        options.forEach(update::set);
        return modify(query, update);
    }


    /**
     * 得到实体class
     *
     * @return {@link Class<T>}
     */
    public Class<T> getEntityClass() {
        Type superClass = this.getClass().getGenericSuperclass();
        Type[] actualTypeArguments = ((ParameterizedType) superClass).getActualTypeArguments();
        return (Class<T>) actualTypeArguments[0];
    }


}
